home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / ast_comp / pfcthash.txt < prev    next >
Text File  |  1993-07-04  |  32KB  |  1,183 lines

  1. #! /bin/sh
  2. # This is a shell archive.  Remove anything before this line, then unpack
  3. # it by saving it into a file and typing "sh file".  To overwrite existing
  4. # files, type "sh file -c".  You can also feed this as standard input via
  5. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  6. # will see the following message at the end:
  7. #        "End of shell archive."
  8. # Contents:  perf.c
  9. # Wrapped by oz@yunexus on Fri Dec 14 12:08:56 1990
  10. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  11. if test -f 'perf.c' -a "${1}" != "-c" ; then 
  12.   echo shar: Will not clobber existing file \"'perf.c'\"
  13. else
  14. echo shar: Extracting \"'perf.c'\" \(29699 characters\)
  15. sed "s/^X//" >'perf.c' <<'END_OF_FILE'
  16. X
  17. X/*
  18. X *            P e r f e c t   H a s h
  19. X *
  20. X *
  21. X *    Program to search for Minimal Perfect Hash Functions for
  22. X *    use in lexical analyzers. C.D. Havener Jan 26 1982
  23. X *    GenRad Inc. 37 Great Road, Bolton Mass. 01740
  24. X *    Based on paper "Minimal Perfect Hash Functions Made Simple"
  25. X *    by Richard J. Cichelli - Comm. of ACM Jan 1980 pp 17-19
  26. X *
  27. X *    Synopsis: The hash function is h = assoc value of 1st
  28. X *    letter + length of keyword + assoc value of last letter
  29. X *
  30. X *    This program finds the associated values of the letters
  31. X *    given a list of keywords, 1 per line. It works most of
  32. X *    the time for up to about 40 keywords but certain
  33. X *    pathological cases exist. A semi-perfect hash is usually
  34. X *    found by the program. The user can then tighten the
  35. X *    default limits for max associated char value or the
  36. X *    table limit using the -v and -t options. Sometimes the
  37. X *    presort heuristics actually make the search process
  38. X *    much more difficult. The user can try his luck at manual
  39. X *    sorting using the -n option. Since the hash function
  40. X *    produces such a limited range of numbers it can only work
  41. X *    for up to about 40 keywords. If a language needs say 80
  42. X *    keywords just split them up into two tables and let the
  43. X *    lexical analyzer look in first one then the other, this
  44. X *    will still be much faster than any other keyword lookup.
  45. X *
  46. X *    This program has run sucessfully on a VAX 11/780 under
  47. X *    4.1BSD and VMS (using Vax-11 C and Decus C).
  48. X */
  49. X
  50. X/*)BUILD
  51. X    $(MP) = 1            # uses macros with arguments
  52. X    $(STACK)    = 10000        # lots of recursion
  53. X    $(TKBOPTIONS)    = {
  54. X        STACK    = 1024
  55. X        TASK    = ...PRF
  56. X    }
  57. X*/
  58. X
  59. X#ifdef    DOCUMENTATION
  60. Xtitle    perfect        Find Perfect Hash Functions
  61. Xindex    perfect        Find perfect hash functions
  62. X
  63. Xsynopsis
  64. X
  65. X    perfect [-options]
  66. X
  67. Xdescription
  68. X
  69. X    perfect reads a list of keywords from the standard input
  70. X    and computes a "perfect hash" function for the set.
  71. X    The following options are defined:
  72. X    .lm +8
  73. X    .s.i -8;-d######Enable debug code.
  74. X    .s.i -8;-n######Disable pre-sort of keywords.  (See below).
  75. X    .s.i -8;-t#<n>##Limit the maximum table size to <n> entries.
  76. X    .s.i -8;-v#<n>##Limit the maximum associated character value.
  77. X    .s.i -8;-k#<n>##Use only the first <n> keywords in the list.
  78. X    .s.i -8;-a#<n>##Give a status report every <n> seconds.
  79. X    (Unix and Vax-11C/VMS only).
  80. X    .s.i -8;-o#file#Write a sample keyword lookup routine to the
  81. X    indicated file.
  82. X    .s.lm -8
  83. X    The program prints a running commentary on the standard
  84. X    output.
  85. X
  86. XAuthor
  87. X
  88. X    Charlie Havener, GenRad Inc.  Bolton Mass.
  89. X
  90. X    (Modified by Martin Minow, Dec, Maynard Mass.)
  91. X
  92. XDiscussion
  93. X
  94. X    This technical note describes an implementation of a
  95. X    pragmatic algorithm for finding perfect or semi-perfect hash
  96. X    functions for lists of keywords. The resulting hash function can
  97. X    be used to speed up lexical analyzers used in translators and
  98. X    compilers. The algorithm was described by R.J. Cichelli in The
  99. X    January 1980 issue of Communications of the ACM under the title
  100. X    "Minimal Perfect Hash Functions made Simple." The article did not
  101. X    include a computer language description of the algorithm and some
  102. X    important implementation details were unclear. It is assumed that
  103. X    the user will know what to do with the output of this program.
  104. X    The -o option may be used to produce a lexical analyser kernel
  105. X    from the tables built by this program. Another reference for
  106. X    those wishing to pursue the topic further is "More On Minimal
  107. X    Perfect Hash tables" by Curtis Cook and R. Oldehoeft, a Colorado
  108. X    State University Technical Report.
  109. X
  110. X    The program takes a list of keywords and sorts them in such
  111. X    a way that the search time for a hash function will be
  112. X    reasonable. Once sorted a recursive trial and error procedure
  113. X    hunts for a hash function satisfying user supplied bounds
  114. X    of table size and associated character value limits.  The
  115. X    built-in hash function is
  116. X
  117. X        hash = assoc value of first character
  118. X        + keyword length
  119. X        + assoc value of last character
  120. X
  121. X    It is critically important to select a good ordering of the
  122. X    keywords before searching begins. I ran up 100 hours of VAX time
  123. X    searching for a hash function with an unordered list, and gave
  124. X    up. Once the sort heuristics were debugged it found the hash
  125. X    function in minutes. Typically it will find the function in
  126. X    minutes or you may as well give up. A status reporting feature is
  127. X    built into the program on the UNIX system that lets you follow
  128. X    the progress of the search depth. If it has trouble, you can
  129. X    tell just which word it can't get past, and take appropriate
  130. X    action. If it has trouble you can attempt to alter its choice of
  131. X    pre-ordering by moving troublesome words to the front of the
  132. X    list. In a sequel to his paper, Cichelli stated that sometimes
  133. X    the sort heuristics makes the search longer. There is also no
  134. X    guarantee that a hash function can be found. The program does the
  135. X    obvious precheck that no two keywords have the same first and
  136. X    last letter and length in common (e.g. BAK KAB). Nonetheless,
  137. X    as pointed out by Jaeschke and Osterberg in an overly harsh
  138. X    criticism, there are many pathological sets of keywords that fail.
  139. X    (On Cichelli's minimal perfect hash functions method. Comm. ACM
  140. X    Dec. 1980, pp 728-729) The algorithm also only works for up to
  141. X    about 40 keywords due to the limited range of numbers the formula
  142. X    can generate. If you have, say 80 keywords, just make two hash
  143. X    tables and look in each one. Here are some examples of the
  144. X    program's output:
  145. X    .s.nf
  146. X    Perfect hash function finder, CDH Ver. 2.9
  147. X    Start time = Mon Oct 17 20:40:40 1983
  148. X    28 keywords, 19 distinct letters.
  149. X    The associated char value limit is 19
  150. X    The table size limit is 100
  151. X    The search ordering is
  152. X     else double continue case float struct sizeof
  153. X     static short extern typedef default register for
  154. X     char while entry int if return do unsigned switch
  155. X     union goto auto long break
  156. X    .s
  157. X    Success! Associated Char Values Follow:
  158. X     a =19, b = 4, c = 1, d = 0, e = 0, f = 3, g =17,
  159. X     h =14, i =17, k =19, l = 6, n = 8, o = 0, r =11,
  160. X     s = 6, t = 0, u =16, w =13, y =14,
  161. X    Hash min = 2, max = 30, spread = 29
  162. X     do 2, else 4, case 5, double 6, default 7,
  163. X     float 8, continue 9, typedef 10, short 11, struct 12,
  164. X     static 13, extern 14, sizeof 15, char 16, for 17,
  165. X     while 18, entry 19, int 20, goto 21, if 22, auto 23,
  166. X     unsigned 24, return 25, switch 26, long 27, break 28,
  167. X     union 29, register 30,
  168. X    .s
  169. X    Total search() invocations = 15913
  170. X     Started Mon Oct 17 20:40:40 1983
  171. X    Finished Mon Oct 17 20:40:42 1983
  172. X    .s.nf
  173. X    Perfect hash function finder, CDH Ver. 2.9
  174. X    Start time = Mon Oct 17 20:41:11 1983
  175. X    39 keywords, 19 distinct letters.
  176. X    The associated char value limit is 19
  177. X    The table size limit is 100
  178. X    The search ordering is
  179. X     TEXT RESET TRUE REWRITE READLN SQRT SQR EOLN TRUNC
  180. X     PUT EXP PAGE CHR CHAR COS SUCC READ ROUND DISPOSE
  181. X     PRED SIN OUTPUT ORD INPUT INTEGER GET MAXINT REAL
  182. X     WRITE EOF FALSE NEW WRITELN LN ARCTAN ABS BOOLEAN
  183. X     PACK UNPACK
  184. X    .s
  185. X    Success! Associated Char Values Follow:
  186. X     A =18, B =11, C =16, D =18, E = 3, F = 3, G = 0,
  187. X     I = 1, K =19, L = 9, M =18, N =19, O = 8, P = 9,
  188. X     R = 0, S =14, T = 0, U =13, W =19,
  189. X    Hash min = 3, max = 45, spread = 43
  190. X     GET 3, TEXT 4, RESET 5, INPUT 6, TRUE 7,
  191. X     INTEGER 8, EOF 9, REWRITE 10, FALSE 11, PUT 12,
  192. X     REAL 13, OUTPUT 14, EXP 15, PAGE 16, SQR 17, SQRT 18,
  193. X     CHR 19, CHAR 20, TRUNC 21, READ 22, ROUND 23,
  194. X     MAXINT 24, READLN 25, EOLN 26, WRITE 27, DISPOSE 28,
  195. X     ORD 29, LN 30, PRED 31, PACK 32, COS 33, SUCC 34,
  196. X     ABS 35, SIN 36, BOOLEAN 37, UNPACK 38, NEW 41,
  197. X     ARCTAN 43, WRITELN 45,
  198. X    .s
  199. X    Total search() invocations = 149292
  200. X     Started Mon Oct 17 20:41:11 1983
  201. X    Finished Mon Oct 17 20:41:35 1983
  202. X    .s 2.f
  203. X    Usually, the first time you run perf, just let everything
  204. X    default. The second time,
  205. X    use the -t option to limit the table size to the first hash
  206. X    value plus the number of keywords.
  207. X
  208. X    On Unix and Vax/VMS (Vax-11 C), The program will accept
  209. X    SIGTERM signals (CTRL/C on VMS) for an update status report
  210. X    since it may take quite a while to find the hash function values.
  211. X
  212. XSample keyword tables
  213. X
  214. X    The following tables are known to work correctly.
  215. X    The first defines the keywords for the C programming
  216. X    language; the second for a toy computer language.
  217. X
  218. X    int char float double struct union long short
  219. X    unsigned auto extern register typedef static
  220. X    goto return sizeof break continue if else for
  221. X    do while switch case default entry
  222. X
  223. X    GET TEXT RESET OUTPUT MAXINT INPUT TRUE
  224. X    INTEGER EOF REWRITE FALSE CHR CHAR TRUNC REAL
  225. X    SQR SQRT WRITE PUT ORD READ ROUND READLN EXP
  226. X    PAGE EOLN COS SUCC DISPOSE NEW ABS LN BOOLEAN
  227. X    WRITELN SIN PACK UNPACK ARCTAN PRED
  228. X
  229. X#endif
  230. X
  231. X#include    <stdio.h>
  232. X#include    <ctype.h>
  233. X
  234. X#define    EOS    '\0'
  235. X#define    FALSE    0
  236. X#define    TRUE    1
  237. X#define    VOID    int
  238. X
  239. X#ifdef    unix
  240. X#define    UNIX    1
  241. X#define    DOALARM    1
  242. X#endif
  243. X
  244. X#ifndef    vms
  245. X#define    VMS    0
  246. X#else
  247. X#define    VMS    1
  248. X#define    DOALARM    1
  249. X#endif
  250. X
  251. X#ifndef    UNIX
  252. X#define    UNIX    0
  253. X#endif
  254. X
  255. X#ifndef    DOALARM
  256. X#define    DOALARM    0
  257. X#endif
  258. X
  259. X#if    UNIX
  260. X#define    IO_ERROR    1
  261. X#endif
  262. X
  263. X#if    VMS
  264. X#include    <ssdef.h>
  265. X#define    IO_ERROR    SS$_ABORT
  266. X/*
  267. X * This creates text files in vanilla RMS on VMS
  268. X */
  269. Xextern FILE    *fdopen();
  270. X#define    CREATE(f, m) fdopen(creat(f, 0, "rat=cr", "rfm=var"), m)
  271. X#else
  272. X#define    CREATE    fopen
  273. X#endif
  274. X
  275. X#if    DOALARM
  276. X#include    <signal.h>
  277. Xextern int    status();
  278. X#endif
  279. X
  280. X#define    MAXKEYS        100        /* Maximum number of keys    */
  281. X#define MAXCHARS    0377        /* Maximum number of char's    */
  282. X#define UNDEF        -1        /* the undefined value        */
  283. X
  284. Xtypedef struct keyword {
  285. X    int        len;        /* Keyword length            */
  286. X    char    last;        /* Last byte of keyword            */
  287. X    char    word[1];    /* Keyword text                */
  288. X} KEYWORD;
  289. X
  290. X/*
  291. X * Define some frequently used operations as macros:
  292. X *    hash(p)        returns the hash value for this keyword
  293. X *    used(n)        TRUE if this hash value is in use or illegal
  294. X *    defined(c)    TRUE if this character is defined
  295. X */
  296. X
  297. X#define hash(p)        (value[p->word[0]] + p->len + value[p->last])
  298. X#define    used(n)        ((n > tablesize || n < 0) ? TRUE : mapped[n])
  299. X#define    defined(c)    (c != UNDEF)
  300. X
  301. Xchar    cval[MAXCHARS];        /* All possible characters        */
  302. Xshort    cused[MAXCHARS];    /* count of often char used        */
  303. Xshort    order[MAXKEYS];        /* ordering of key words by subscript    */
  304. Xshort    neworder[MAXKEYS];    /* the new supposedly improved ordering    */
  305. Xshort    hashval[MAXKEYS];    /* current hashvalue of the key word    */
  306. Xshort    value[MAXCHARS];    /* associated value of the character    */
  307. Xint    mapped[MAXKEYS];    /* track which table entries are in use    */
  308. Xchar    name[50];        /* bigger than any keyword should be    */
  309. X
  310. X
  311. XKEYWORD        *keywds[MAXKEYS];
  312. X
  313. Xextern long    time();
  314. Xextern char    *ctime();
  315. Xextern char    *malloc();
  316. Xextern int    aredefined();
  317. Xextern char    *strchr();
  318. X
  319. Xint    debug        = 0;
  320. Xint    nkeys        = sizeof(keywds) / (sizeof (KEYWORD));
  321. Xint    tablesize    = sizeof(keywds) / (sizeof (KEYWORD));
  322. Xshort    trys        = 0;
  323. Xint    nletters    = 0;
  324. Xshort    kilotrys    = 0;
  325. Xint    atime        = 10 * 60;    /* default alarm status 10 min.    */
  326. Xchar    *klimit        = NULL;
  327. Xchar    *textp        = NULL;
  328. Xlong    bigcount    = 0;
  329. Xshort    depth        = 0;
  330. Xshort    k_now        = 0;        /* value of k for status report    */
  331. Xint    nosort        = FALSE;    /* -n sets nosort TRUE        */
  332. Xlong    start, stop;            /* the start and finish times    */
  333. Xshort    vlimit        = 0;
  334. Xshort    keylimit    = 0;
  335. Xshort    tlimit        = 0;
  336. Xchar    *output        = NULL;        /* Output file if set        */
  337. X
  338. Xchar    letters[37];            /* string of defined chars    */
  339. Xchar    *letend = letters;        /* -> free space in letters[]    */
  340. X
  341. Xmain(argc,argv)
  342. Xint        argc;
  343. Xchar        *argv[];
  344. X{
  345. X    getoptions(argc, argv);
  346. X    start = time(NULL);
  347. X#if    DOALARM
  348. X    signal(SIGALRM,status);
  349. X    signal(SIGTERM,status);        /* status on kill -TERM pid    */
  350. X    alarm(atime);            /* status every N secs        */
  351. X#endif
  352. X    setup();
  353. X    dosort();
  354. X    printf("The search ordering is\n");
  355. X    prntorder();
  356. X    if (search(0)) {
  357. X#if    DOALARM
  358. X        alarm(0);
  359. X#endif
  360. X        printf("\nSuccess! Associated Char Values Follow:\n");
  361. X        prntvals();
  362. X        prnthash();
  363. X        printf("\n");
  364. X        if (output != NULL)
  365. X        dooutput();
  366. X    }
  367. X    else {
  368. X#if    DOALARM
  369. X        alarm(0);
  370. X#endif
  371. X        printf("\nFailed to find char values for hash function\n");
  372. X    }
  373. X    printf("Total search invocations = %ld, max depth = %d\n",
  374. X        bigcount, depth);
  375. X    stop = time(NULL);
  376. X    printf(" Started %s", ctime(&start));
  377. X    printf("Finished %s", ctime(&stop));
  378. X}
  379. X
  380. Xsetup()
  381. X{
  382. X    register KEYWORD    *kp;
  383. X    register int        c;
  384. X    register int        i;
  385. X    int            len;
  386. X
  387. X    for (i = 0; (scanf("%s", name)) != EOF; i++) {
  388. X        if (i >= MAXKEYS) {
  389. X        printf("Too many keys, %d max.\n", MAXKEYS);
  390. X        exit(IO_ERROR);
  391. X        }
  392. X        len = strlen(name);
  393. X        kp = (KEYWORD *) malloc(sizeof (KEYWORD) + len);
  394. X        if (kp == NULL) {
  395. X        printf("Out of room allocating keywords\n");
  396. X        exit(IO_ERROR);
  397. X        }
  398. X        keywds[i] = kp;
  399. X        kp->len = len;
  400. X        kp->last = name[len - 1];
  401. X        strcpy(&kp->word[0], name);
  402. X    }
  403. X    nkeys = (keylimit == 0) ? i : keylimit;
  404. X
  405. X    for (i = 0; i < MAXKEYS; i++) {
  406. X        hashval[i] = UNDEF;
  407. X        order[i] = i;
  408. X        mapped[i] = FALSE;
  409. X    }
  410. X
  411. X    for (i = 0; i < MAXCHARS; i++) {
  412. X        cval[i] = 0;
  413. X        value[i] = UNDEF;
  414. X    }
  415. X
  416. X    if (!precheck()) {
  417. X        printf("Perfect hash search terminated\n");
  418. X        exit(IO_ERROR);
  419. X    }
  420. X
  421. X    for (i = 0; i < nkeys; i++) {
  422. X        c = keywds[i]->word[0];    /* Get first char of keyword    */
  423. X        cval[c] = c;        /* Remember this one used    */
  424. X        if (cused[c] == 0)        /* If it's the first time,    */
  425. X        ++nletters;        /* Count unique letters        */
  426. X        ++cused[c];            /* count how often letter used    */
  427. X        c = keywds[i]->last;    /* Get last char of keyword    */
  428. X        cval[c] = c;        /* And do the same for it    */
  429. X        if (cused[c] == 0)
  430. X        ++nletters;
  431. X        ++cused[c];
  432. X    }
  433. X
  434. X    tablesize = (tlimit == 0 ? MAXKEYS : tlimit);
  435. X    printf("Perfect hash function finder, CDH Ver. 2.9\n");
  436. X    printf("Start time = %s", ctime(&start));
  437. X    printf("%d keywords, %d distinct letters.\n",
  438. X        nkeys, nletters);
  439. X    nletters = (vlimit > 0) ? vlimit : nletters;
  440. X    printf("The associated char value limit is %d\n", nletters);
  441. X    printf("The table size limit is %d\n", tablesize);
  442. X
  443. X    /*
  444. X     * You should make tablesize at least nkeys + 1 since the
  445. X     * first value is usually 1 or 2 even if both assoc char
  446. X     * values are zero since the keyword length is included!
  447. X     */
  448. X}
  449. X
  450. Xdosort()
  451. X{
  452. X    register KEYWORD    *kp;
  453. X    register int        i, j;
  454. X    int            k, m;
  455. X    int            newvalues;
  456. X
  457. X    if (nosort) {
  458. X        printf("No presorting of keywords.\n");
  459. X        return;
  460. X    }
  461. X
  462. X    /*
  463. X     * first order by sum of frequencies of occurrences of each
  464. X     * keys 1st and last letter
  465. X     */
  466. X
  467. X    for (i = 0; i < nkeys; i++) {
  468. X        kp = keywds[i];
  469. X        order[i] = cused[kp->word[0]] + cused[kp->last];
  470. X    }
  471. X    for (m = 0; m < nkeys; m++) {
  472. X        for (k = -1, i = 0; i < nkeys; i++) {
  473. X        if (order[i] > k) {
  474. X            k = order[i];
  475. X            j = i;        /* remember keywd subscript    */
  476. X        }
  477. X        }
  478. X        order[j] = 0;
  479. X        neworder[m] = j;
  480. X    }
  481. X    for (i=0; i < nkeys; i++)
  482. X        order[i] = neworder[i];
  483. X    if (debug > 2) {
  484. X        printf("After first ordering\n");
  485. X        prntorder();
  486. X    }
  487. X
  488. X    /*
  489. X     * The second ordering follows, keywds whose values are
  490. X     * defined by keywds earlier in the order are placed
  491. X     * immediately after they are defined. This causes hash
  492. X     * value conflicts to occur as early during the search
  493. X     * as possible.
  494. X     */
  495. X
  496. X    letend = letters;
  497. X    letters[0] = EOS;
  498. X    merge(order[0]);            /* prime the pump    */
  499. X    neworder[0] = order[0];
  500. X    order[0] = UNDEF;
  501. X    for (i = 1; i < nkeys;) {
  502. X        for (newvalues = TRUE; newvalues;) {
  503. X        newvalues = FALSE;
  504. X        for (k = 0; k < nkeys; k++) {
  505. X            if (order[k] == UNDEF)
  506. X            continue;
  507. X            if (aredefined(order[k])) {
  508. X            neworder[i++] = order[k];
  509. X            order[k] = UNDEF;
  510. X            continue;
  511. X            }
  512. X        }
  513. X        for (k = 0; k < nkeys; k++) {
  514. X            if (order[k] != UNDEF) {
  515. X            neworder[i++] = order[k];
  516. X            merge(order[k]);
  517. X            order[k] = UNDEF;
  518. X            newvalues = TRUE;
  519. X            break;
  520. X            }
  521. X        }
  522. X        }
  523. X    }
  524. X    for (i = 0; i < nkeys; i++)
  525. X        order[i] = neworder[i];
  526. X    if (precheck() == FALSE) {
  527. X        printf("OOPS - call a Guru, the presort botched it\n");
  528. X        prntorder();
  529. X        exit(IO_ERROR);
  530. X    }
  531. X}
  532. X
  533. X/*
  534. X * merge - adds keywd letters to the string of those defined.
  535. X * This could be speeded up, but it's not a critical-path function.
  536. X */
  537. X
  538. XVOID
  539. Xmerge(n)
  540. Xint        n;
  541. X{
  542. X    register KEYWORD    *kp;
  543. X
  544. X    kp = keywds[n];
  545. X    if (debug > 2)
  546. X        printf("merging in %s\n", kp->word);
  547. X    if (strchr(letters, kp->word[0]) == NULL) {
  548. X        *letend++ = kp->word[0];
  549. X        *letend = EOS;
  550. X    }
  551. X    if (strchr(letters, kp->last) != NULL) {
  552. X        *letend++ = kp->last;
  553. X        *letend = EOS;
  554. X    }
  555. X}
  556. X
  557. X/*
  558. X * aredefined - see if 1st & last char of keywd are defined
  559. X */
  560. X
  561. Xint
  562. Xaredefined(n)
  563. Xint        n;
  564. X{
  565. X    register KEYWORD    *kp;
  566. X
  567. X    kp = keywds[n];
  568. X    if (strchr(letters, kp->word[0]) != NULL
  569. X     && strchr(letters, kp->last) != NULL)
  570. X        return (TRUE);
  571. X    else return (FALSE);
  572. X}
  573. X
  574. X/*
  575. X * precheck - all keywds length,1st and last char disjoint
  576. X */
  577. X
  578. Xint
  579. Xprecheck()
  580. X{
  581. X    int            pretest;
  582. X    register KEYWORD    *ip, *jp;
  583. X    short            i, j;
  584. X    short            m, k;
  585. X    char            a, b;
  586. X
  587. X    pretest = TRUE;
  588. X    for (m = 0; m < nkeys; m++) {
  589. X        i = order[m];
  590. X        ip = keywds[i];
  591. X        a = ip->word[0];
  592. X        b = ip->last;
  593. X        for (k = m + 1; k < nkeys-1; k++) {
  594. X        j = order[k];
  595. X        jp = keywds[j];
  596. X        if (ip->len == jp->len
  597. X         && ((a == jp->word[0] && b == jp->last)
  598. X          || (a == jp->last && b == jp->word[0]))) {
  599. X            pretest = FALSE;
  600. X            printf("Precheck fails on %s and %s\n",
  601. X            ip->word, jp->word);
  602. X        }
  603. X        }
  604. X    }
  605. X    return (pretest);
  606. X}
  607. X
  608. X/*
  609. X * prntorder - printout the current order of the keywords
  610. X */
  611. X
  612. XVOID
  613. Xprntorder()
  614. X{
  615. X    register int    i, j;
  616. X
  617. X    for (i = 0, j = 0; i < nkeys; i++) {
  618. X        if ((j + keywds[order[i]]->len) >= 60) {
  619. X        printf("\n");
  620. X        j = 0;
  621. X        }
  622. X        printf(" %s", keywds[order[i]]->word);
  623. X        j += keywds[order[i]]->len + 1;
  624. X    }
  625. X    printf("\n");
  626. X}
  627. X
  628. X/*
  629. X * prntvals - prints out the letter associated values
  630. X */
  631. X
  632. Xprntvals()
  633. X{
  634. X    register int    i, j;
  635. X
  636. X    for (i = 0, j = 0; i < MAXCHARS; i++) {
  637. X        if (cval[i]) {
  638. X        printf("%s %c =%2d,",
  639. X            ((++j % 10) == 0) ? "\n" : "",
  640. X            cval[i], value[i]);
  641. X        }
  642. X    }
  643. X    printf("\n");
  644. X}
  645. X
  646. X/*
  647. X * prnthash - prints out the hash values for the keywds
  648. X */
  649. X
  650. XVOID
  651. Xprnthash()
  652. X{
  653. X    register int        i, j;
  654. X    register KEYWORD    *kp;
  655. X    int            swap;
  656. X    int            hmin;
  657. X    int            hmax;
  658. X    int            spread;
  659. X
  660. X
  661. X    swap = TRUE;
  662. X    hmin = MAXKEYS;
  663. X    hmax = 0;
  664. X    spread = 0;
  665. X    for (i = 0; i < nkeys; i++) {
  666. X        j = hashval[i] = hash(keywds[i]);
  667. X        hmin = (hmin < j) ? hmin : j;
  668. X        hmax = (hmax > j) ? hmax : j;
  669. X        order[i] = i;
  670. X    }
  671. X    while (swap) {        /* plain vanilla bubble sort */
  672. X        swap = FALSE;
  673. X        for (i = 0; i < nkeys-1; i++) {
  674. X        if (hashval[order[i+1]] < hashval[order[i]]) {
  675. X            swap = TRUE;
  676. X            j = order[i];
  677. X            order[i] = order[i+1];
  678. X            order[i+1] = j;
  679. X        }
  680. X        }
  681. X    }
  682. X    printf("Hash min = %d, max = %d, spread = %d\n",
  683. X        hmin, hmax, hmax - hmin + 1);
  684. X    for (i=0, j=0; i < nkeys; i++, j++) {
  685. X        kp = keywds[order[i]];
  686. X        if (j  + (kp->len + 5) > 60) {
  687. X        printf("\n");
  688. X        j = 0;
  689. X        }
  690. X        printf(" %s %d,", kp->word,
  691. X        hash(keywds[order[i]]));
  692. X        j += (kp->len + 5);
  693. X    }
  694. X    printf("\n");
  695. X}
  696. X
  697. X/*
  698. X * search - calls itself recursively to find char values
  699. X */
  700. X
  701. Xint
  702. Xsearch(k)
  703. Xregister int    k;
  704. X{
  705. X    register KEYWORD    *p;
  706. X    register int        j;
  707. X    int            m;
  708. X    short            v1, v2, num;
  709. X    short            sub1, sub2, subn;
  710. X    int            thesame;
  711. X
  712. X    thesame = FALSE;
  713. X    bigcount++;
  714. X    k_now = k;            /* global for status reporting    */
  715. X    if (k >= nkeys)            /* hey - we may be all done    */
  716. X        return (TRUE);
  717. X    if (k > depth)            /* global for status reporting    */
  718. X        depth = k;            /* keep track of search depth    */
  719. X    m = order[k];
  720. X    p = keywds[m];
  721. X    sub1 = p->word[0];        /* sub1 = first letter in word    */
  722. X    sub2 = p->last;            /* sub2 = last letter in word    */
  723. X    if (sub1 == sub2)
  724. X        thesame = TRUE;
  725. X    v1 = value[sub1];
  726. X    v2 = value[sub2];
  727. X    if (defined(v1) && defined(v2)) {
  728. X        num = hash(p);        /* Both letters defined        */
  729. X        if (used(num))
  730. X        return (FALSE);        /* this hash value is in use    */
  731. X        else {
  732. X        hashval[m] = num;    /* install it            */
  733. X        mapped[num] = TRUE;
  734. X        if (search(k + 1))
  735. X            return (TRUE);
  736. X        else {
  737. X            hashval[m] = UNDEF;
  738. X            mapped[num] = FALSE;
  739. X            return (FALSE);
  740. X        }
  741. X        }
  742. X    }
  743. X    else if (defined(v1)) {
  744. X        for (j = 0; j <= nletters; j++) {
  745. X        v2 = j;
  746. X        num = v1 + p->len + v2;
  747. X        if (!used(num)) {
  748. X            hashval[m] = num;
  749. X            mapped[num] = TRUE;
  750. X            value[sub2] = v2;
  751. X            subn = sub2;
  752. X            if (search(k + 1))
  753. X            return (TRUE);
  754. X            else
  755. X            remove(m, sub2);
  756. X        }
  757. X        }
  758. X        return (FALSE);
  759. X    }
  760. X    else if (defined(v2)) {
  761. X        for (j = 0; j <= nletters; j++) {
  762. X        v1 = j;
  763. X        num = v1 + p->len + v2;
  764. X        if (!used(num)) {
  765. X            hashval[m] = num;
  766. X            mapped[num] =TRUE;
  767. X            value[sub1] = v1;
  768. X            subn = sub1;
  769. X            if (search(k + 1))
  770. X            return (TRUE);
  771. X            else
  772. X            remove(m, sub1);
  773. X        }
  774. X        }
  775. X        return (FALSE);
  776. X    }
  777. X    else {                /* neither defined        */
  778. X        for (j = 0; j <= nletters; j++) {
  779. X        if (thesame) {
  780. X            v1 = v2 = j;
  781. X            num = v1 + p->len + v2;
  782. X            if (!used(num)) {
  783. X            hashval[m] = num;
  784. X            mapped[num] = TRUE;
  785. X            value[sub1] = v1;    /* same as value[sub2]    */
  786. X            subn = sub1;
  787. X            if (search(k + 1))
  788. X                return (TRUE);
  789. X            else
  790. X                remove(m, subn);
  791. X            }
  792. X        }
  793. X        else {
  794. X            value[sub1] = j;
  795. X            if (search(k))        /* if never TRUE thru    */
  796. X            return (TRUE);        /* for loop, then FALSE */
  797. X            else
  798. X            value[sub1] = UNDEF;
  799. X        }
  800. X        }
  801. X        return (FALSE);
  802. X    }
  803. X}
  804. X
  805. X/*
  806. X * remove - backup by deleting keywds hash value etc
  807. X */
  808. X
  809. XVOID
  810. Xremove(m, subn)
  811. Xregister short    m;
  812. Xregister short    subn;
  813. X{
  814. X    if (debug > 6)
  815. X        printf("removing %s, subn = %d\n", keywds[m]->word, subn);
  816. X    mapped[hashval[m]] = FALSE;
  817. X    hashval[m] = UNDEF;
  818. X    value[subn] = UNDEF;
  819. X}
  820. X
  821. X/*
  822. X * dooutput writes parser tables to the indicated output file.
  823. X */
  824. X
  825. Xchar    *function[] = {
  826. X    "",
  827. X    "int",
  828. X    "keyword(text)",
  829. X    "register char\t*text;",
  830. X    "/*",
  831. X    " * Look for keyword (string of alpha) in the perfect hash table",
  832. X    " * Return the index (L_xxx value) or 0 if not found",
  833. X    " */",
  834. X    "{",
  835. X    "\tregister char\t*tp;",
  836. X    "\tregister int\thash;",
  837. X    "",
  838. X    "\tif (*text < FIRST || *text > LAST)",
  839. X    "\t    return (0);",
  840. X    "\tfor (tp = text; isalpha(*tp); tp++)",
  841. X    "\t    ;",
  842. X    "\thash = (tp - text);",
  843. X    "\tif (*--tp < FIRST || *tp > LAST)",
  844. X    "\t    return (0);",
  845. X    "\thash += (px_assoc - FIRST)[*text] + (px_assoc - FIRST)[*tp];",
  846. X    "\tif (px_table[hash] == NULL)",
  847. X    "\t    return (0);",
  848. X    "\tif (strncmp(text, px_table[hash], (tp - text + 1)) != 0)",
  849. X    "\t    return (0);",
  850. X    "\treturn(hash);",
  851. X    "}",
  852. X    "",
  853. X    NULL,
  854. X};
  855. X
  856. Xdooutput()
  857. X{
  858. X    FILE        *fd;
  859. X    register char    **funp;
  860. X    register int    i;
  861. X    int        first, last, hval;
  862. X
  863. X    if ((fd = CREATE(output, "w")) == NULL) {
  864. X        perror(output);
  865. X        return;
  866. X    }
  867. X    fprintf(fd, "#include <stdio.h>\n");
  868. X    fprintf(fd, "#include <ctype.h>\n");
  869. X    for (i = 0; i < nkeys; i++) {
  870. X        fprintf(fd, "#define\tL_%s\t", keywds[order[i]]->word);
  871. X        if (keywds[order[i]]->len < 14)
  872. X        putc('\t', fd);
  873. X        if (keywds[order[i]]->len <  6)
  874. X        putc('\t', fd);
  875. X        fprintf(fd, "%d\n",    hash(keywds[order[i]]));
  876. X    }
  877. X    for (i = MAXCHARS; --i >= 0 && cval[i] == 0;)
  878. X        ;
  879. X    last = i;
  880. X    for (i = 0; i <= last && cval[i] == 0;)
  881. X        i++;
  882. X    first = i;
  883. X    fprintf(fd, "#define FIRST\t'%c'\n", first);
  884. X    fprintf(fd, "#define LAST\t'%c'\n", last);
  885. X    fprintf(fd, "static char px_assoc[] = {\n");
  886. X    while (i <= last) {
  887. X        fprintf(fd, "\t%d,\t/* '%c' */\n", value[i], i);
  888. X        i++;
  889. X    }
  890. X    fprintf(fd, "};\n");
  891. X    fprintf(fd, "static char *px_table[] = {\n");
  892. X    last = 0;
  893. X    for (i = 0; i < nkeys; i++) {
  894. X        hval = hash(keywds[order[i]]);
  895. X        while (last < hval) {
  896. X        fprintf(fd, "\tNULL,\t\t\t/*%3d\t*/\n", last);
  897. X        last++;
  898. X        }
  899. X        fprintf(fd, "\t\"%s\",\t", keywds[order[i]]->word);
  900. X        if (keywds[order[i]]->len < 13)
  901. X        putc('\t', fd);
  902. X        if (keywds[order[i]]->len <  5)
  903. X        putc('\t', fd);
  904. X        fprintf(fd, "/*%3d\t*/\n", hval);
  905. X        last = hval + 1;
  906. X    }
  907. X    fprintf(fd, "};\n");
  908. X    for (funp = function; *funp != NULL; funp++)
  909. X        fprintf(fd, "%s\n", *funp);
  910. X    fclose(fd);
  911. X}
  912. X
  913. X#if    DOALARM
  914. X/*
  915. X * status - on signal this reports the current statistics
  916. X */
  917. X
  918. XVOID
  919. Xstatus()
  920. X{
  921. X    fprintf(stderr,
  922. X        "\nSTATUS:  key \"%s\" (%d), search calls = %ld, max depth = %d\n",
  923. X        keywds[k_now]->word, k_now, bigcount, depth);
  924. X    fflush(stderr);
  925. X    signal(SIGTERM,status);
  926. X    signal(SIGALRM,status);
  927. X    alarm(atime);
  928. X}
  929. X#endif
  930. X
  931. X/*
  932. X *            G E T O P T I O N S
  933. X *
  934. X * Generalized command line argument processor.  The following
  935. X * types of arguments are parsed:
  936. X *    flags        The associated int global is incremented:
  937. X *            -f    f-flag set to 1
  938. X *            -f123    f-flag set to 123 (no separator)
  939. X *            -fg    f-flag and g-flag incremented.
  940. X *    values        A value must be present.  The associated
  941. X *            int global receives the value:
  942. X *            -v123    value set to 123
  943. X *            -v 123    value set to 123
  944. X *    arguments    The associated global (a char *) is
  945. X *            set to the next argument:
  946. X *            -f foo    argument set to "foo"
  947. X */
  948. X
  949. X#define    FLAG    0
  950. X#define    VALUE    1
  951. X#define    ARG    2
  952. X#define    ERROR    3
  953. X
  954. Xtypedef struct argstruct {
  955. X    char    opt;        /* Option byte            */
  956. X    char    type;        /* FLAG/VALUE/ARG        */
  957. X    char    *name;        /* What to set if option seen    */
  958. X    char    *what;        /* String for error message    */
  959. X} ARGSTRUCT;
  960. X
  961. Xstatic ARGSTRUCT arginfo[] = {
  962. X{ 'd',    FLAG,    (char *)&debug,        "debug" },
  963. X{ 'a',    VALUE,    (char *)&atime,        "alarm time for status" },
  964. X{ 't',    VALUE,    (char *)&tlimit,    "table size limit" },
  965. X{ 'v',    VALUE,    (char *)&vlimit,    "associated value limit" },
  966. X{ 'k',    VALUE,    (char *)&keylimit,    "keyword limit" },
  967. X{ 'n',    FLAG,    (char *)&nosort,    "no sort wanted" },
  968. X{ 'o',    ARG,    (char *)&output,    "parser output file" },
  969. X{ EOS,    ERROR,    NULL,            NULL },
  970. X};
  971. X
  972. Xstatic char *argtype[] = {
  973. X    "flag", "takes value", "takes argument"
  974. X};
  975. X
  976. Xstatic
  977. Xgetoptions(argc, argv)
  978. Xint        argc;
  979. Xchar        **argv;
  980. X/*
  981. X * Process arg's
  982. X */
  983. X{
  984. X    register char        *ap;
  985. X    register int        c;
  986. X    register ARGSTRUCT    *sp;
  987. X    int            i;
  988. X    int            helpneeded;
  989. X
  990. X    getredirection(argc, argv);
  991. X    helpneeded = FALSE;
  992. X    for (i = 1; i < argc; i++) {
  993. X        if ((ap = argv[i]) != NULL && *ap == '-') {
  994. X        argv[i] = NULL;
  995. X        for (ap++; (c = *ap++) != EOS;) {
  996. X            if (isupper(c))
  997. X            c = tolower(c);
  998. X            sp = arginfo;
  999. X            while (sp->opt != EOS && sp->opt != c)
  1000. X            sp++;
  1001. X            switch (sp->type) {
  1002. X            case FLAG:            /* Set the flag    */
  1003. X            if (!isdigit(*ap)) {
  1004. X                (*((int *)sp->name))++;
  1005. X                break;
  1006. X            }
  1007. X            case VALUE:            /* -x123    */
  1008. X                if (isdigit(*ap)) {
  1009. X                *((int *)sp->name) = atoi(ap);
  1010. X                *ap = EOS;
  1011. X            }
  1012. X            else if (*ap == EOS && ++i < argc) {
  1013. X                *((int *)sp->name) = atoi(argv[i]);
  1014. X                argv[i] = NULL;
  1015. X            }
  1016. X            else {
  1017. X                fprintf(stderr,
  1018. X                "Bad option '%c%s' (%s)",
  1019. X                c, ap, sp->what);
  1020. X                fprintf(stderr, ", ignored\n");
  1021. X                helpneeded++;
  1022. X            }
  1023. X            break;
  1024. X
  1025. X            case ARG:            /* -x foo    */
  1026. X            if (++i < argc) {
  1027. X                *((char **) sp->name) = argv[i];
  1028. X                argv[i] = NULL;
  1029. X            }
  1030. X            else {
  1031. X                fprintf(stderr,
  1032. X                "Argument needed for '%c' (%s)",
  1033. X                c, sp->what);
  1034. X                fprintf(stderr, ", ignored\n");
  1035. X                helpneeded++;
  1036. X            }
  1037. X            break;
  1038. X
  1039. X            case ERROR:
  1040. X            fprintf(stderr,
  1041. X                "Unknown option '%c', ignored\n", c);
  1042. X            helpneeded++;
  1043. X            break;
  1044. X            }
  1045. X        }
  1046. X        }
  1047. X    }
  1048. X    if (helpneeded > 0) {
  1049. X        for (sp = arginfo; sp->opt != EOS; sp++) {
  1050. X        fprintf(stderr, "'%c' -- %s (%s)\n",
  1051. X            sp->opt, sp->what, argtype[sp->type]);
  1052. X        }
  1053. X    }
  1054. X}
  1055. X
  1056. X/*
  1057. X * getredirection() is intended to aid in porting C programs
  1058. X * to VMS (Vax-11 C) which does not support '>' and '<'
  1059. X * I/O redirection.  With suitable modification, it may
  1060. X * useful for other portability problems as well.
  1061. X */
  1062. X
  1063. X#include    <stdio.h>
  1064. X
  1065. Xgetredirection(argc, argv)
  1066. Xint        argc;
  1067. Xchar        **argv;
  1068. X/*
  1069. X * Process vms redirection arg's.  Exit if any error is seen.
  1070. X * If getredirection() processes an argument, argv[i], it is changed
  1071. X * to NULL.
  1072. X *
  1073. X * Warning: do not try to simplify the code for vms.  The code
  1074. X * presupposes that getredirection() is called before any data is
  1075. X * read from stdin or written to stdout.
  1076. X *
  1077. X * Normal usage is as follows:
  1078. X *
  1079. X *    main(argc, argv)
  1080. X *    int        argc;
  1081. X *    char        *argv[];
  1082. X *    {
  1083. X *        register int        i;
  1084. X *        int            nargs;
  1085. X *
  1086. X *        getredirection(argc, argv);    ** setup redirection
  1087. X *        for (nargs = 0, i = 1; i < argc, i++) {
  1088. X *            if (argv[i] == NULL)    ** skip if processed
  1089. X *            continue;        ** by getredirection()
  1090. X *            nargs++;            ** here is an argument
  1091. X *            ...                ** process argv[i]
  1092. X *        }
  1093. X *        if (nargs == 0) {        ** no arguments given
  1094. X *            ...
  1095. X *        }
  1096. X *    }
  1097. X */
  1098. X{
  1099. X#ifdef    vms
  1100. X    register char        *ap;    /* Argument pointer    */
  1101. X    int            i;    /* argv[] index        */
  1102. X    int            file;    /* File_descriptor     */
  1103. X    extern int        errno;    /* Last vms i/o error     */
  1104. X
  1105. X    for (i = 1; i < argc; i++) {    /* Do all arguments    */
  1106. X        if (*(ap = argv[i]) == '<') {  /* <file        */
  1107. X        if (freopen(++ap, "r", stdin) == NULL) {
  1108. X            perror(ap);        /* Can't find file    */
  1109. X            exit(errno);    /* Is a fatal error    */
  1110. X        }
  1111. X        goto erase_arg;        /* Ok, erase argument    */
  1112. X        }
  1113. X        else if (*ap++ == '>') {    /* >file or >>file    */
  1114. X        if (*ap == '>') {    /* >>file        */
  1115. X            /*
  1116. X             * If the file exists, and is writable by us,
  1117. X             * call freopen to append to the file (using the
  1118. X             * file's current attributes).  Otherwise, create
  1119. X             * a new file with "vanilla" attributes as if
  1120. X             * the argument was given as ">filename".
  1121. X             * access(name, 2) is TRUE if we can write on
  1122. X             * the specified file.
  1123. X             */
  1124. X            if (access(++ap, 2) == 0) {
  1125. X            if (freopen(ap, "a", stdout) == NULL) {
  1126. X                perror(ap);
  1127. X                exit(errno);
  1128. X            }
  1129. X            else goto erase_arg;
  1130. X            }            /* If file accessable    */
  1131. X            else ;        /* Else it's just >file    */
  1132. X        }
  1133. X        /*
  1134. X         * On vms, we want to create the file using "standard"
  1135. X         * record attributes.  create(...) creates the file
  1136. X         * using the caller's default protection mask and
  1137. X         * "variable length, implied carriage return"
  1138. X         * attributes. dup2() associates the file with stdout.
  1139. X         */
  1140. X        if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
  1141. X         || dup2(file, fileno(stdout)) == -1) {
  1142. X            perror(ap);        /* Can't create file    */
  1143. X            exit(errno);    /* is a fatal error    */
  1144. X        }            /* If '>' creation    */
  1145. Xerase_arg:    argv[i] = NULL;        /* red. erases argument    */
  1146. X        }                /* If redirection    */
  1147. X    }                /* For all arguments    */
  1148. X#endif
  1149. X#ifdef    decus
  1150. X    argc = argv[0];            /* Supress warning msg.    */
  1151. X#endif
  1152. X}
  1153. X
  1154. X#if    UNIX
  1155. X/*
  1156. X * The following is missing on some Unix systems
  1157. X */
  1158. X
  1159. Xchar *
  1160. Xstrchr(string, c)
  1161. Xregister char    *string;
  1162. Xregister char    c;
  1163. X/*
  1164. X * If 'c' is in string, return a pointer to it.
  1165. X * Else, return NULL.
  1166. X */
  1167. X{
  1168. X    do {
  1169. X        if (*string == c)
  1170. X        return (string);
  1171. X    } while (*string++ != EOS);
  1172. X    return (NULL);
  1173. X}
  1174. X#endif
  1175. END_OF_FILE
  1176. if test 29699 -ne `wc -c <'perf.c'`; then
  1177.     echo shar: \"'perf.c'\" unpacked with wrong size!
  1178. fi
  1179. # end of 'perf.c'
  1180. fi
  1181. echo shar: End of shell archive.
  1182. exit 0
  1183.